package model

import (
	"database/sql"
	"fmt"
	"strings"
	"time"

	"github.com/tal-tech/go-zero/core/stores/sqlc"
	"github.com/tal-tech/go-zero/core/stores/sqlx"
	"github.com/tal-tech/go-zero/core/stringx"
	"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)

var (
	procTaskFieldNames          = builderx.RawFieldNames(&ProcTask{})
	procTaskRows                = strings.Join(procTaskFieldNames, ",")
	procTaskRowsExpectAutoSet   = strings.Join(stringx.Remove(procTaskFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
	procTaskRowsWithPlaceHolder = strings.Join(stringx.Remove(procTaskFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
)

type (
	ProcTaskModel interface {
		Insert(data ProcTask) (sql.Result, error)
		FindOne(id int64) (*ProcTask, error)
		Update(data ProcTask, tx ...sqlx.Session) error
		Delete(id int64) error
		FindAllByProcInstIdStepStatus(procInstId int64, step int64, status int64) ([]ProcTask, error)
	}

	defaultProcTaskModel struct {
		conn  sqlx.SqlConn
		table string
	}

	ProcTask struct {
		Id         int64     `db:"id"`
		ProcInstId int64     `db:"proc_inst_id"`
		ProcExecId int64     `db:"proc_exec_id"`
		ActorId    string    `db:"actor_id"` // 任务处理人
		Key        string    `db:"key"`
		Group      string    `db:"group"`
		Title      string    `db:"title"`
		Form       string    `db:"form"`
		Status     int64     `db:"status"` // 任务状态 0=关闭，1=开启
		Step       int64     `db:"step"`   // 任务在执行流中的位置
		Pass       int64     `db:"pass"`
		CreateTime time.Time `db:"create_time"`
		UpdateTime time.Time `db:"update_time"`
	}
)

func NewProcTaskModel(conn sqlx.SqlConn) ProcTaskModel {
	return &defaultProcTaskModel{
		conn:  conn,
		table: "`proc_task`",
	}
}

func (m *defaultProcTaskModel) Insert(data ProcTask) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, procTaskRowsExpectAutoSet)
	ret, err := m.conn.Exec(query, data.ProcInstId, data.ProcExecId, data.ActorId, data.Key, data.Group, data.Title, data.Form, data.Status, data.Step, data.Pass)
	return ret, err
}

func (m *defaultProcTaskModel) FindOne(id int64) (*ProcTask, error) {
	query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", procTaskRows, m.table)
	var resp ProcTask
	err := m.conn.QueryRow(&resp, query, id)
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultProcTaskModel) Update(data ProcTask, tx ...sqlx.Session) error {
	query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, procTaskRowsWithPlaceHolder)
	var err error
	if tx != nil && len(tx) > 0 && tx[0] != nil {
		_, err = tx[0].Exec(query, data.ProcInstId, data.ProcExecId, data.ActorId, data.Key, data.Group, data.Title, data.Form, data.Status, data.Step, data.Pass, data.Id)
	} else {
		_, err = m.conn.Exec(query, data.ProcInstId, data.ProcExecId, data.ActorId, data.Key, data.Group, data.Title, data.Form, data.Status, data.Step, data.Pass, data.Id)
	}
	return err
}

func (m *defaultProcTaskModel) Delete(id int64) error {
	query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
	_, err := m.conn.Exec(query, id)
	return err
}

func (m *defaultProcTaskModel) FindAllByProcInstIdStepStatus(procInstId int64, step int64, status int64) ([]ProcTask, error) {
	query := fmt.Sprintf("select %s from %s where `proc_inst_id` = ? and `step` = ? and `status` = ?", procTaskRows, m.table)
	resp := make([]ProcTask, 0)
	err := m.conn.QueryRows(&resp, query, procInstId, step, status)
	switch err {
	case nil:
		return resp, nil
	case sqlc.ErrNotFound:
		return resp, nil
	default:
		return nil, err
	}
}
